/*
*  RC4 implementation.
*  (Porting from Linux source code)
*/

#include "dna_common.h"

#define RC4_KEY_SIZE    16

/* Streaming encryption (RC4) */
typedef unsigned char	BYTE;
typedef BYTE *          PBYTE;
typedef unsigned int	UINT;
typedef struct {
    UINT ux;
    UINT uy;
    BYTE abystate[256];
} RC4Ext, * PRC4Ext;

/* RC4 encryption, S-BOX initialization */
static void __rc4_init(PRC4Ext pRC4, PBYTE pbyKey, UINT cbKey_len)
{
    UINT  ust1, ust2;
    UINT  keyindex;
    UINT  stateindex;
    PBYTE pbyst;
    UINT  idx;

    pbyst = pRC4->abystate;
    pRC4->ux = 0;
    pRC4->uy = 0;
    for (idx = 0; idx < 256; idx++)
        pbyst[idx] = (BYTE)idx;
    keyindex = 0;
    stateindex = 0;
    for (idx = 0; idx < 256; idx++) {
        ust1 = pbyst[idx];
        stateindex = (stateindex + pbyKey[keyindex] + ust1) & 0xff;
        ust2 = pbyst[stateindex];
        pbyst[stateindex] = (BYTE)ust1;
        pbyst[idx] = (BYTE)ust2;
        if (++keyindex >= cbKey_len)
            keyindex = 0;
    }
}

/* RC4 dynamic key calculation */
static UINT __rc4_byte(PRC4Ext pRC4)
{
    UINT ux;
    UINT uy;
    UINT ustx, usty;
    PBYTE pbyst;

    pbyst = pRC4->abystate;
    ux = (pRC4->ux + 1) & 0xff;
    ustx = pbyst[ux];
    uy = (ustx + pRC4->uy) & 0xff;
    usty = pbyst[uy];
    pRC4->ux = ux;
    pRC4->uy = uy;
    pbyst[uy] = (BYTE)ustx;
    pbyst[ux] = (BYTE)usty;

    return pbyst[(ustx + usty) & 0xff];
}

/* RC4 encryption and decryption entity:
* For data check, we add 'sum' param to here.
*/
static void __rc4_crypt(PRC4Ext pRC4, PBYTE pbyDest,
                PBYTE pbySrc, UINT cbData_len, UINT * sum)
{
    UINT ii;
    for (ii = 0; ii < cbData_len; ii++) {
        pbyDest[ii] = (BYTE)(pbySrc[ii] ^ __rc4_byte(pRC4));
        if(sum != NULL) {
            (*sum) += pbyDest[ii];
        }
    }
}

/** RC4 encrypt/decrypt
 */
int dna_rc4_encrypt(
        unsigned char * dst, unsigned char * src,
        unsigned int len, unsigned char * key, unsigned int * sum)
{
    RC4Ext local_RC4_Ext;
    __rc4_init(&local_RC4_Ext, key, RC4_KEY_SIZE);
    __rc4_crypt(&local_RC4_Ext, dst, src, len, sum);

    return len;
}

